package com.zh.jwt.common.jwt;

import com.auth0.jwt.JWTSigner;
import com.auth0.jwt.JWTVerifier;
import com.auth0.jwt.internal.com.fasterxml.jackson.databind.ObjectMapper;
import com.zh.jwt.common.constant.ServiceConstant;

import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.TimeUnit;

/**
 * @author hezihao
 * @version 1.0
 * <p>
 * @date 2020/9/17 6:32 下午
 * <p>
 * JWT工具类
 */
public class JwtUtil {
    private static final String EXP = "exp";
    private static final String PAYLOAD = "payload";
    /**
     * 过期时间，一个月
     */
    private static final long EXPIRED_TIME = TimeUnit.DAYS.toMillis(30);

    private static final String KEY_USER_ID = "userId";

    private JwtUtil() {
    }

    /**
     * 生成Token
     *
     * @param userId 用户Id
     */
    public static String createToken(Long userId) {
        Map info = new HashMap<>();
        info.put(KEY_USER_ID, userId);
        return createToken(info, EXPIRED_TIME, ServiceConstant.SERVER_SECRET);
    }

    /**
     * 加密生成token
     *
     * @param object 载体信息
     * @param maxAge 有效时长
     * @param secret 服务器私钥
     * @return Token令牌
     */
    private static <T> String createToken(T object, long maxAge, String secret) {
        try {
            final JWTSigner signer = new JWTSigner(secret);
            final Map<String, Object> claims = new HashMap<>();
            ObjectMapper mapper = new ObjectMapper();
            String jsonString = mapper.writeValueAsString(object);
            claims.put(PAYLOAD, jsonString);
            claims.put(EXP, System.currentTimeMillis() + maxAge);
            return signer.sign(claims);
        } catch (Exception e) {
            return null;
        }
    }

    /**
     * 验证Token是否有效
     *
     * @param token 令牌
     */
    public static boolean verifierToken(String token) {
        Long userId = getUserIdByToken(token);
        return userId != null && userId > 0;
    }

    /**
     * 解析token
     *
     * @param token  加密后的token字符串
     * @param clazz  解密后的对象类型
     * @param secret 服务器私钥
     * @return 如果token验证成功返回classT类型的对象，如果验证失败或过期返回null
     */
    private static <T> T parseToken(String token, Class<T> clazz, String secret) {
        final JWTVerifier verifier = new JWTVerifier(secret);
        try {
            final Map<String, Object> claims = verifier.verify(token);
            if (claims.containsKey(EXP) && claims.containsKey(PAYLOAD)) {
                long exp = (Long) claims.get(EXP);
                long currentTimeMillis = System.currentTimeMillis();
                //没有过期
                if (currentTimeMillis < exp) {
                    String json = (String) claims.get(PAYLOAD);
                    ObjectMapper objectMapper = new ObjectMapper();
                    return objectMapper.readValue(json, clazz);
                }
            }
            return null;
        } catch (Exception e) {
            return null;
        }
    }

    /**
     * 从Token中获取UserId
     *
     * @param token 令牌
     */
    public static Long getUserIdByToken(String token) {
        Map map = parseToken(token, Map.class, ServiceConstant.SERVER_SECRET);
        if (map == null) {
            return null;
        }
        return Long.parseLong((map.get(KEY_USER_ID)).toString());
    }
}